package org.jetbrains.plugins.scala.caches

import com.intellij.openapi.util.ModificationTracker
import org.jetbrains.plugins.scala.caches.CachesUtil.CacheCapabilties._
import org.jetbrains.plugins.scala.caches.CachesUtil.Timestamped
import org.jetbrains.plugins.scala.caches.stats.{CacheTracker, Tracer}

import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.{ConcurrentHashMap, ConcurrentMap}

private class CacheN[T <: Product, R](id: String, name: String, modificationTracker: => ModificationTracker) {
  // Code generated by @Cached macro annotation, method with parameters

  private[this] val mapAndCounter: AtomicReference[Timestamped[ConcurrentMap[T, Tuple1[R]]]] =
    CacheTracker.track(id, name)(new AtomicReference(Timestamped(null, -1L))) // TODO (timestampedMapCacheCapabilities)

  def apply(key: T)(f: => R): R = {
    val currModCount = modificationTracker.getModificationCount
    val tracer = Tracer(id, name)
    tracer.invocation()
    val map = {
      val timestampedMap = mapAndCounter.get
      if (timestampedMap.modCount < currModCount) {
        mapAndCounter.compareAndSet(timestampedMap, Timestamped(new ConcurrentHashMap(), currModCount))
      }
      mapAndCounter.get.data
    }
    map.get(key) match {
      case Tuple1(v) => v
      case null =>
        val stackStamp = RecursionManager.markStack()
        tracer.calculationStart()
        val computed = try {
          f
        } finally {
          tracer.calculationEnd()
        }
        if (stackStamp.mayCacheNow()) {
          val race = map.putIfAbsent(key, Tuple1(computed))
          if (race != null) race._1 else computed
        } else {
          computed
        }
    }
  }

/*
  @Cached(ModTracker.anyScalaPsiChange)
  def foo(x: Int): String = "Foo"

  new _root_.scala.volatile();
  private val org$example$Example$foo$$mapAndCounter: _root_.java.util.concurrent.atomic.AtomicReference[_root_.org.jetbrains.plugins.scala.caches.CachesUtil.Timestamped[_root_.java.util.concurrent.ConcurrentMap[Int, _root_.scala.Some[String]]]] = {
    import _root_.org.jetbrains.plugins.scala.caches.CachesUtil.CacheCapabilties._;
    _root_.org.jetbrains.plugins.scala.caches.stats.CacheTracker.track("org$example$Example$foo$cacheKey", "Example.foo")(new _root_.java.util.concurrent.atomic.AtomicReference(_root_.org.jetbrains.plugins.scala.caches.CachesUtil.Timestamped(null, -1L)))
  };
  def foo(x: Int): String = {
    def org$example$Example$foo$$cachedFun(): String = "Foo";
    val currModCount = ModTracker.anyScalaPsiChange.getModificationCount();
    val org$example$Example$foo$$tracer = _root_.org.jetbrains.plugins.scala.caches.stats.Tracer("org$example$Example$foo$cacheKey", "Example.foo");
    org$example$Example$foo$$tracer.invocation();
    val key = x;
    val map = {
      val timestampedMap = org$example$Example$foo$$mapAndCounter.get;
      if (timestampedMap.modCount.$less(currModCount))
        org$example$Example$foo$$mapAndCounter.compareAndSet(timestampedMap, _root_.org.jetbrains.plugins.scala.caches.CachesUtil.Timestamped(new _root_.java.util.concurrent.ConcurrentHashMap(), currModCount))
      else
        ();
      org$example$Example$foo$$mapAndCounter.get.data
    };
    map.get(key) match {
      case Some((v @ _)) => v
      case null => {
        val stackStamp = org.jetbrains.plugins.scala.caches.RecursionManager.markStack();
        org$example$Example$foo$$tracer.calculationStart();
        val computed = try {
          _root_.scala.Some(org$example$Example$foo$$cachedFun())
        } finally org$example$Example$foo$$tracer.calculationEnd();
        if (stackStamp.mayCacheNow())
          {
            val race = map.putIfAbsent(key, computed);
            if (race.$bang$eq(null))
              race.get
            else
              computed.get
          }
        else
          computed.get
      }
    }
  };
  ()
*/
}
